import os
import sys
import config
import smtplib
from copy import deepcopy
from email.mime.text import MIMEText
from email.header import Header
from time import strftime
import traceback
import contextlib
import requests


class MsgChain:
	def __init__(self, **kwargs):
		self.content = []
		if 't' in kwargs.keys():
			self.addPlain(kwargs['t'])

	def addSection(self, section):
		self.content.append(section)

	def addPlain(self, msg):
		self.addSection({'type': 'Plain', 'text': msg})
		return self

	def append(self, chain):
		self.content.extend(chain.content)
		return self

	def strip(self):
		if not self.content:
			return
		cpy = deepcopy(self.content)
		if self.content[0]['type'] == 'Plain':
			cpy[0]['text'] = cpy[0]['text'].lstrip(config.utils.msgChain.stripChars)
		if self.content[-1]['type'] == 'Plain':
			cpy[-1]['text'] = cpy[-1]['text'].rstrip(config.utils.msgChain.stripChars)
		chain = MsgChain()
		chain.content = cpy
		return chain

def log(*objects, sep=' ', end='\n', file=config.utils.log.file,
		print_=config.utils.log.print, flush=config.utils.log.flush):
	if print:
		print(strftime('%Y-%m-%d %H:%M:%S'), *objects, sep=sep, end=end, flush=flush)
	with open(file, 'a', encoding='utf-8') as f:
		print(strftime('%Y-%m-%d %H:%M:%S'), *objects, sep=sep, end=end, file=f, flush=flush)


def LOG(*objects, sep=' ', end='\n', file=config.utils.LOG.file,
		print_=config.utils.LOG.print, flush=config.utils.LOG.flush):
	if print:
		print(strftime('%Y-%m-%d %H:%M:%S'), *objects, sep=sep, end=end, flush=flush)
	with open(file, 'a', encoding='utf-8') as f:
		print(strftime('%Y-%m-%d %H:%M:%S'), *objects, sep=sep, end=end, file=f, flush=flush)


def mail(content, receivers=config.utils.mail.admins, subject=config.utils.mail.subject):
	msg = MIMEText(content, 'plain', 'utf-8')
	msg['From'] = Header(config.utils.mail.from_, 'utf-8')
	msg['To'] = Header(config.utils.mail.to, 'utf-8')
	msg['Subject'] = Header(subject, 'utf-8')
	try:
		smtpObj = smtplib.SMTP()
		smtpObj.connect(config.utils.mail.host, config.utils.mail.port)
		smtpObj.login(config.utils.mail.user, config.utils.mail.pass_)
		smtpObj.sendmail(config.utils.mail.sender, receivers, msg.as_string())
		smtpObj.quit()
	# log(config.utils.mailSucceedMsg)
	except Exception:
		LOG(config.utils.mailFailMsg)


@contextlib.contextmanager
def trying(toStop=config.utils.trying.toStop, toLOG=config.utils.trying.toLOG,
		   toLog=config.utils.trying.toLog, toMail=config.utils.trying.toMail,
		   toMsg=config.utils.trying.toMsg):
	try:
		yield
	except Exception:
		s = config.utils.trying.exceptionMsg + '\n' + traceback.format_exc()
		if toLOG:
			LOG(s)
		elif toLog:
			log(s)
		if toMail:
			mail(s)
		if toMsg:
			chain = MsgChain()
			chain.addPlain(s)
			sendFriends(chain, config.admins)
		if toStop:
			exit(-1)


def par(page, **kwargs):
	page += '?'
	for key, val in kwargs.items():
		page += f'{key}={val}&'
	return page[:-1]


def getRaw(page, **kwargs):
	# log(page, kwargs)
	headers = {
		'Connection': 'close',
	}
	r = requests.get(config.utils.requestPrefix + page, json=kwargs, headers=headers)
	# log(r.text)
	return r


def postRaw(page, **kwargs):
	# log(page, kwargs)
	headers = {
		'Connection': 'close',
	}
	r = requests.post(config.utils.requestPrefix + page, json=kwargs, headers=headers)
	# log(r.text)
	return r


def get(page, **kwargs):
	result = getRaw(page, **kwargs).json()
	if result == {'code': 0, 'msg': 'success', 'messageId': -1}:
		LOG(config.utils.bannedMsg)
	return result


def post(page, **kwargs):
	result = postRaw(page, **kwargs).json()
	if result == {'code': 0, 'msg': 'success', 'messageId': -1}:
		LOG(config.utils.bannedMsg)
	return result

def sendFriends(chain, *args):
	friends = args
	if isinstance(chain, str):
		chain = MsgChain(t=chain)
	if not isinstance(chain, MsgChain):
		chain = MsgChain(t=str(chain))
	for friend in friends:
		with trying():
			if type(friend) == str:
				friend = where(friend)
			post('sendFriendMessage', target=friend, messageChain=chain.strip().content)

def sendGroups(chain, *args):
	groups = args
	if isinstance(chain, str):
		chain = MsgChain(t=chain)
	if not isinstance(chain, MsgChain):
		chain = MsgChain(t=str(chain))
	for group in groups:
		with trying():
			if type(group) == str:
				group = where(group)
			if group in config.utils.shadowGroups:
				continue
			post('sendGroupMessage', target=group, messageChain=chain.strip().content)

def sendFriendsAndGroups(chain, friends=[], groups=[]):
	sendFriends(chain, *friends)
	sendGroups(chain, *groups)

def autoReply(chain, gum):
	if gum.currentMessage['type'] == 'FriendMessage':
		sendFriends(chain, gum.currentMessage['sender']['id'])
	elif gum.currentMessage['type'] == 'GroupMessage':
		sendGroups(chain, gum.currentMessage['sender']['group']['id'])

autoGum = None

def setAuto(gum):
	global autoGum
	autoGum = gum

def auto(chain):
	if not autoGum:
		return
	autoReply(chain, autoGum)

def shouldShadow(bub, msg):
	with trying():
		if 'NudgeBack' in str(type(bub)) \
				and msg['type'] == 'NudgeEvent' \
				and msg['subject']['kind'] == 'Group' \
				and msg['subject']['id'] in config.utils.shadowGroups:
			return True
	return False

def here():
	msg = autoGum.currentMessage
	if msg['type'] == 'FriendMessage':
		return msg['sender']['id']
	if msg['type'] == 'GroupMessage':
		return msg['sender']['group']['id']
	return None

def where(name):
	ls = []
	for friend in get('friendList')['data']:
		if name in friend['nickname']:
			ls.append(friend)
	for group in get('groupList')['data']:
		if name in group['name']:
			ls.append(group)
	if len(ls) > 1:
		return
	return ls[0]['id']